//
// message.h
// libprotea
// Tools Library for the Protea Project / Message handling between processes
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

// message.h

#ifndef __PROTEA_MESSAGE_H__
#define __PROTEA_MESSAGE_H__

const long MAX_ROUTES = 4;

/**
	\file message.h
	\brief Message Class

	Includes definitions of the XCToolsMessage class which is the basis of all messages
	sent between any tools processes
*/

#include "../OSDep/process.h"

// Forward class declarations
class XCAppInterface;
class XCMsgPort;



long Align( long size );


// 
// message enumerations and constants
//
///////////////////////////////////////////////////////////////////////////////////////
enum message_type 
{
	// Initialization message
	msg_init = 0,

	// User message
	msg_user = 1,

	// Admin message
	msg_admin = 2,

	// Shutdown message
	msg_shutdown = 3,

	// Status message
	msg_status = 4,

	// Transaction message
	msg_trans = 5,

	// Dispatch message
	msg_dispatch = 6,

	// Transaction response message
	msg_trans_response = 7,

	// Set Options for a process message
	msg_options = 8,

	// SQL Query message
	msg_sql_query = 9,

	// Last message (not used in code, only for verification)
	msg_last = 10
};



//
// Dispatcher opcodes
//
///////////////////////////////////////////////////////////////////////////////////////
enum disp_opcode
{
	// start substation, dispatcher and app
	disp_start_app = 0,
	// shutdown all
	disp_shutdown = 1,
	// dispatcher to contact ui on port specified
	disp_contact_ui = 2,
	// application to return information to GCS
	disp_query_info = 3,
	// query reply information
	disp_query_reply = 4,
	// all processes to return information to GCS
	disp_pushdown_query = 5,
	// ???
	disp_pushdown_start = 6,	
	disp_shutdown_confirm = 7,

	disp_hello = 8,
	disp_goodbye = 9,

	disp_security_change = 10,

	// Signal application to abandon transaction records for this id
	disp_abort_txn = 11		
};

#define msg_src (XCMsgPort*)NULL		// routing info for message source



// route location for sdflag
enum route_loc
{
	src_dest_clear = 0,	// &&

	// GCS source
	src_gcs = 1,		// ||
	// Application source
	src_app = 2,		// ||
	// Interface source
	src_int = 4,		// ||

	// GDS destination
	dest_gcs = 16,		// ||
	// Application destination
	dest_app = 32,		// ||
	// Interface destination
	dest_int = 64,		// ||
	// Everything destionation
	dest_all = 112		// ||
};

enum port_type
{
	pt_misc = 0,		// misc., unknown
	pt_listen = 1,		// listener socket
	pt_gcs = 2,		// connection to GCS
	pt_sub = 3,		// connection to Substation
	pt_disp = 4,		// connection to Dispatcher
	pt_app = 5,		// connection to Application
	pt_ui = 6		// connection to User Interface
};

// Enumeration for the misc. flag value in base_message
enum misc_value
{
	// Cleared
	misc_clear = 0,		// &&	
	misc_clear_other = 15,	// &&	

	// Broadcast / directed message
	misc_broadcast = 16,	// ||
	// Query / response message
	misc_query = 32,	// ||
	// Is this is a job transaction?
	misc_job_txn = 64,	// ||

	misc_clear_count = 112	// &&
};

//
// Message classes
//
//////////////////////////////////////////////////////////////////////////////////////


// Structure of route info records
struct route_info 
{
	// Source handle
	long src;

	// Destination handle
	long dest;
};



// All messages in the Tools system start with this header.
struct base_message {

	// Size of the actual data
	long data_size;

	// Type of message being send
	message_type type;

	// Source/Destination flags
	unsigned char sdflag;

	// Misc. flags
	unsigned char miscflag;

	// Size of the original data (compression)
	long orig_size;

	// Routing table
	route_info routes[4];
};
#define	BASE_MESSAGE_SIZE	sizeof( base_message )




/*
	Tracks all memory internally for the message

	All message header information can be obtained using GetHeaderPtr.

	Processes should use the GetDataPtr function in order to read/write specific
	data to the 'data' portion of the message.
*/
class __EXPORT XCToolsMessage : public XCBase
{
public:
	// Constructor
	XCToolsMessage( XCAppInterface* owner );

	// Destructor
	virtual ~XCToolsMessage();

	XCToolsMessage* Copy();

	// Attempt to compress data
	void Compress();
	void Decompress();
	bool UsesCompression() const {
		return msg->data_size != msg->orig_size;
	};
	void* GetCompressPtr() { if (compbuf) return compbuf; else return GetDataPtr(); };

	// Create a message based on a given size
	void CreateMsg( long size );

	// Create a message based on given header information (and copies header info)
	void CreateMsg( base_message* header );

	// Set header information
	void SetHeader( message_type type, route_loc src, route_loc dest, bool broadcast, bool query );

	// Set header information (type only)
	void SetHeader( message_type type );

	// Change Query/Response flag
	void SetQueryResponse( bool query = true );

	// Adds an entry into the routing table
	void AddRouteInfo( const XCMsgPort* src, const XCMsgPort* dest );

	// Removes the last entry from the routing table
	void RemoveRouteInfo();

	// Removes all routes from the routing table
	void ClearRoutes();

	// Returns a pointer to the last entry in the routing table
	route_info* PeekLastRoute() const;

	// Copy routing information from another message
	void CopyRouteInfo( const XCToolsMessage& src );

	// SetReply?
	void SetReply( const XCToolsMessage& src );

	// Clear the message from the container
	void DeleteMsg();

	// Returns a pointer to the header information (base_message)
	base_message* GetHeaderPtr() const;

	// Returns a pointer to the data portion of the message
	void* GetDataPtr() const;

	// Get full size (including route info)
	long GetDataFullSize() const;

	// Debug dump
	void Dump();

private:
	// Alters the size of a message
	void ChangeMsgSize( long size );

	// Used in the dump procedure
	unsigned char GetMsgChar( unsigned long offset );

	// Pointer to the actual message
	base_message* msg;

	// Pointer to compression/decompression buffer
	char* compbuf;

	// Pointer to the message owner (used for ToolsTrace)
	XCAppInterface* myowner;
};


class disp_message
{
public:
	long appnbr;	// Application number
	long opcode;
	char data[1];
};
#define DISP_MESSAGE_SIZE	(offsetof( disp_message, data ) + BASE_MESSAGE_SIZE)



class contact_ui_message
{
public:
	long appnum;
	char appname[32];
	long port;
	char host[1];
};
#define CONTACT_UI_MESSAGE_SIZE	(offsetof( contact_ui_message, host ) + BASE_MESSAGE_SIZE)




class startup_message
{
public:
	long appnum;
	long pid;
	long filelen;
	char data[1];
};
#define STARTUP_MESSAGE_SIZE (offsetof( startup_message, data ) + BASE_MESSAGE_SIZE)


class status_message
{
public:
	long pid;
	char filename[32];
	long listenport;
	long parentpid;
	bool start;
	long trans_complete;
	long trans_open;
	long trans_stated;

	long msg_sent;
	long msg_recv;

	double cpu_time;

	int debug_type_filter;
	int debug_level_cutoff[ num_tc ];

	char hostname[1];
};
#define STATUS_MESSAGE_SIZE (offsetof( status_message, hostname ) + BASE_MESSAGE_SIZE)


class admin_message
{
public:
	long err_num;
	long field_num;
	long src_pid;
	char err_message[1];
};
#define ADMIN_MESSAGE_SIZE (offsetof( admin_message, err_message ) + BASE_MESSAGE_SIZE)


class option_message
{
public:
	pid_t dest_pid;
	int debug_type_filter;
	int debug_level_cutoff;
	bool enabled;
	char hostname[1];
};
#define OPTION_MESSAGE_SIZE (offsetof( option_message, hostname ) + BASE_MESSAGE_SIZE)








enum trans_data_type {
	TRANS_HEADER_INFO = 0,
	TRANS_HEADER_TYPE = 1,
	TRANS_DETAIL_TYPE = 2,
	TRANS_MESSAGE_TYPE = 3,
	QUERY_HEADER_INFO = 4,
	QUERY_FIELD_INFO = 5
};



// Base structure for a transaction/query message
struct trans_query_base {
	long			transid;	// Transaction id
	long			order;		// Message order or total message count (info)
	long			appnbr;		// Application number
	trans_data_type	data_type;	// Data type
};



// Structure of data segment of a transaction message
struct trans_message : trans_query_base {
	long			format;		// Format number or trans number (info)
	char			data[1];	// data (buffer data, ??? (info), trans_error_message struct (error message)
};
//#define TRANS_MESSAGE_SIZE	(offsetof( trans_message, data ) + BASE_MESSAGE_SIZE)
#define TRANS_MESSAGE_SIZE (sizeof( trans_message ) + BASE_MESSAGE_SIZE)




/*
	Structure for returning error messages
	Note: Field number is contained in the format field of the parent trans_message
*/
struct trans_error_message {
	long			number;				// Error number
	short int		severity;			// Message severity (status, warning, error, critical)
	short int		fieldnumber;		// Field number
	char			text[1];			// Message text
};



struct trans_info_message {
	char success;		// Success flag
	char userlen;		// Length of username
	char passlen;		// Length of password
	char action;		// Super transaction action: Nothing=0/Create=1/Commit=2/Rollback=3
	char phase;			// Phase of multi-phase transaction: SinglePhase=0/Phase1=1/Phase2=2, etc.
	long lang;			// Language for the transaction
	long trans_number;	// Super-transaction number
	int session_id;		// Session ID
	char data[1];		// Security information
};





// Structure of data segment of a transaction message
struct sql_query_header_message : trans_query_base {
	char userlen;		// Length of username
	char passlen;		// Length of password
	long format;		// Format number
	char action;		// Distinct mode
	long start_record;	// Record # to start on
	long record_count;	// # of records to retrieve
	int session_id;		// Session id
	char data[1];		// Security information
};
//#define SQL_QUERY_HEADER_SIZE	(offsetof( sql_query_header_message, data ) + BASE_MESSAGE_SIZE)
#define SQL_QUERY_HEADER_SIZE (sizeof( sql_query_header_message ) + BASE_MESSAGE_SIZE)

struct sql_query_result_message : trans_query_base {
	char fieldcount;	// Total number of fields
	char wherecount;	// Total number of where clauses
	char data[1];		// sql_query_field records
};
//#define SQL_QUERY_RESULT_SIZE	(offsetof( sql_query_result_message, data ) + BASE_MESSAGE_SIZE)
#define SQL_QUERY_RESULT_SIZE (sizeof( sql_query_result_message ) + BASE_MESSAGE_SIZE)


struct sql_query_field {
	int number;			// Field number
	long ordernumber;	// Order operator

	// Used for return data
	char valuesize;		// Field data size
	char data[1];		// Field data
};

struct sql_query_where {
	int number;			// Field number
	long compnumber;	// Comparison operator
	char pre;			// "Pre" parentheses
	char post;			// "Post" parentheses
	char logical;		// Logical value (and/or)
	char value1size;	// Size of value1
	char value2size;	// Size of value2
	char data[1];		// Field data
};

//
// Misc structures
//
//////////////////////////////////////////////////////////////////////////////////////


// Structure of an initialization message
struct init_message {
	PORT_HANDLE response;
	PORT_HANDLE close_me[2];

	// Version information
	int major;
	int minor;

	// Parent PID information
	long parentpid;

	app_start_message startup_parms;
};



class disp_num_msg
{
public:
	long	number;
	char	data[1];
};	
//#define TRANS_MESSAGE_SIZE(x) 	(offsetof(trans_message, data) + x)



//
// Message Port (Pipes and/or Sockets)
//
class XCTransport;

class __EXPORT XCMsgPort : public XCBase
{
public:
	XCMsgPort( process_info* pi, XCAppInterface* owner );
	virtual ~XCMsgPort();

	void SetTransport( XCTransport* new_transport );

	XCTransport* transport;			// Transport for the message container

	long received;					// number of bytes of message received
	long expected;					// number of bytes of message expected (total)
	XCToolsMessage* message;		// message for callback functions

	LAST_READ_TYPE lastread;		// number of bytes read in last call
	XCTransport* new_connection;	// Transport for accept call result

	// Async io stuff
	long requests;
	bool killme;

	// additional data
	bool awaiting_header;
	port_type porttype;
	bool ready;
	void* extradata;

	XCMsgPort* next;	

	/// Delete object
	void Release();

	XCAppInterface* myowner;
	process_info* myprocess;

	void AllocateBuffer( int size );
	void ClearBuffer();

	char* GetBufferPtr() { return buffer; };

private:
	char* buffer;		// message buffer
};

typedef XCMsgPort* XCMsgPortPtr;

#endif
